//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "ddk.h"
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include <tfssys.h>
#include <misc.h>
#include <hash.h>
#include <llist.h>
#include <abc.h>
#include <fat.h>
#include <fatfile.h>
#include <fsys.h>
#include <bufcache.h>

//#define ZEEVFS_CONF_PATH_TFS         L"tfs/ktfs.cfg"
//#define ZEEVFS_CONF_PATH_BIN         L"bin/ktfs.cfg"


/*
 * Protection for all FATFS files: everybody can read, only
 * group 1.2 (sys.sys) can write and chmod.
 */

PROT fatfs_prot = {
    2,
    ACC_READ|ACC_EXEC,
    {1,        1},
    {0,        ACC_WRITE|ACC_CHMOD}
};

/*
 * check_rofs()
 *    See if the requested mode should be permitted
 *
 * Sends an error and returns 1 if so; 0 if all is OK.
 */
__inline static int check_rofs(UInt32 m, int rofs)
{
    if (rofs && (m & (ACC_WRITE|ACC_CREATE))) {
        return EROFS;
    }
    return(0);
}

Boolean fat_filesystem::OpenBlkDevice()
{
    return GetDevDriver(pBlkName, &pBlkDev);
}

Boolean fat_filesystem::NewDataStructure()
{
    /*
     * Init our data structures
     */
    BufCache *temp = NULL;

    p_Sharedata = new fatfs_sharedata;
    if (p_Sharedata == NULL) {
        DPRINTF(("ERROR:malloc struct abc error!\n"));
        return FALSE;
    }
    p_Sharedata->AddRef();

    pFat = new fat ;
    if (pFat == NULL) {
        DPRINTF(("ERROR:malloc struct abc error\n"));
        return FALSE;
    }
    pFat->AddRef();

    pAbc = new abc;
    if (pAbc == NULL) {
        DPRINTF(("ERROR:alloc abc disk error\n"));
        return FALSE;
    }
    pAbc->AddRef();

    temp = new BufCache(p_Sharedata);
    if (NULL == temp) {
        DPRINTF(("ERROR:malloc BufCache error\n"));
        return FALSE;
    }
    pAbc->setBufCache(temp);

    pFatDir = new FatDir;
    if (pFatDir == NULL) {
        DPRINTF(("ERROR:alloc abc disk error\n"));
        return FALSE;
    }
    pFatDir->AddRef();

    return TRUE;
}

Boolean fat_filesystem::InitFatfs()
{
    /* A sector buffer */
    char *secbuf = NULL;
    UInt32 uBegSec = 0;
    int ret = 0;
    int bTry = FALSE;

    secbuf = (char *)malloc(sizeof(char) * (p_Sharedata->secsz + 1));
    if (NULL == secbuf) {
        return FALSE;
    }

    do {
        /*
         * Block device is open; read in the first block and verify
         * that it looks like a superblock.
         */
        ECode ec = ReadDevice(
                    pBlkDev, uBegSec * p_Sharedata->secsz, p_Sharedata->secsz, (PByte)secbuf, NULL, NULL);
        if (FAILED(ec)) {
            DPRINTF(("ERROR: Failed to Read from the offset:%d", uBegSec));
            free(secbuf);
            return FALSE;
        }

        p_Sharedata->pBlkDev = pBlkDev;
        p_Sharedata->bpb_base = uBegSec;
        bcopy(secbuf, &p_Sharedata->bootb, sizeof(p_Sharedata->bootb));

        ret = pFat->fat_init(p_Sharedata);
        if (ret) {
            if (bTry) break;
            bTry = TRUE;
            DPRINTF(("WARNING:Init Fat failed & try to read MBR"
                            " partition table.\n"));
            uBegSec = pFat->fat_checkmbr(secbuf);
        }
    } while (ret && uBegSec > 0);

    free(secbuf);
    if (ret) {
        DPRINTF(("ERROR:Init Fat failed!\n"));
        return FALSE;
    }

    pAbc->init_buf(pBlkDev, p_Sharedata);

    if (pFatDir->dir_init(pFat, pAbc, p_Sharedata)) {
        DPRINTF(("ERROR:Dir Init error \n"));
        return FALSE;
    }

    /*
     * Start serving requests for the filesystem
     */

    return TRUE;
}

fat_filesystem ::fat_filesystem(wchar_t *wchFsName, wchar_t *wchBlkDev)
{
    pFsName = wchFsName;
    pBlkName = wchBlkDev;
    pBlkDev = NULL;
    used = FALSE;
    p_Sharedata = NULL;
    pFat = NULL;
    pAbc = NULL;
    pFatDir = NULL;
}

fat_filesystem ::~fat_filesystem()
{
    if (p_Sharedata) {
        p_Sharedata->m_sdCardEjected = 1;
        p_Sharedata->Release();
    }

    if (pFat) {
        pFat->Release();
    }

    if (pAbc) {
        pAbc->Release();
    }

    if (pFatDir) {
        pFatDir->Release();
    }

    if (pBlkDev) {
        pBlkDev->Release();
    }
    pBlkDev = NULL;
    used = FALSE;
}
